﻿using etracks.Helpers;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestCodeGen
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("[START]");

            Run();

            Console.WriteLine("[END]");
            Console.ReadLine();
        }

        private static string indent(int depth)
        {
            char indentCharacter = '\t';
            int repetitionsPerDepth = 1;

            return new string(indentCharacter, repetitionsPerDepth * depth);
        }

        private static string translateDataType(string typeName, string isNullable)
        {
            string result = string.Empty;
            bool isReferenceType = false;

            switch (typeName.ToUpperInvariant())
            {
                case "DATE":
                    result = "DateTime";
                    break;
                case "INTEGER":
                    result = "int";
                    break;
                case "TEXT":
                case "CHARACTER":
                case "CHARACTER VARYING":
                    result = "string";
                    isReferenceType = true;
                    break;
                default:
                    Console.WriteLine("Type: {0} is unhandled", typeName);
                    throw new ArgumentOutOfRangeException("typeName", typeName, "no match found");
            }

            if ((!isReferenceType) && (isNullable.ToUpperInvariant() == "YES"))
                result = result + "?";

            return result;
        }

        private static string translateToCodeName(string name)
        {
            string result = string.Empty;

            char[] working = name.ToCharArray();
            bool upNext = true;

            for (int i = 0; i < working.Length; i++)
            {
                if (working[i] == '_')
                {
                    upNext = true;
                    continue;
                }

                if (upNext)
                {
                    working[i] = Char.ToUpperInvariant(working[i]);
                    upNext = false;
                    continue;
                }
            }

            result = (new string(working)).Replace("_", string.Empty);

            return result;
        }

        public static void Run()
        {
            string output_directory_path = @"C:\test\path";
            string output_file_path = string.Empty;

            string opt_db_catalog = "etracks";
            string opt_db_schema = "public";
            string opt_db_object_type = "BASE TABLE";

            DataTable dt = null;

            // Code Generator for Postgres database
            Dictionary<string, object> databaseScanParameters = new Dictionary<string, object>();

            databaseScanParameters.Add("catalog", opt_db_catalog);   // database 
            databaseScanParameters.Add("schema", opt_db_schema);     // schema
            databaseScanParameters.Add("type", opt_db_object_type);   // type => "BASE TABLE" or "VIEW"

            //
            // The idea here is get a list of tables from the database
            // For each table, get a list of columns for that table
            //

            List<string> tableList = PostgresHelper.GetDataTable(@"SELECT  table_name
FROM    information_schema.tables 
WHERE   table_catalog = :catalog
        AND table_schema = :schema
        AND table_type = :type;
", databaseScanParameters).AsEnumerable().Select(r => r.Field<string>("table_name")).ToList();

            Console.WriteLine("tables found: {0}", tableList.Count());

            // 
            // 
            // 
            // 
            Dictionary<string, object> tableScanParameters = new Dictionary<string, object>();

            tableScanParameters.Add("catalog", opt_db_catalog);   // database 
            tableScanParameters.Add("schema", opt_db_schema);     // schema
            tableScanParameters.Add("name", string.Empty);   // name

            // 
            //
            foreach (string tableName in tableList)
            {
                tableScanParameters["name"] = tableName;

                dt = PostgresHelper.GetDataTable(@"SELECT  column_name, data_type, is_nullable
FROM    information_schema.columns
WHERE   table_catalog = :catalog
        AND table_schema = :schema
        AND table_name LIKE :name
ORDER BY ordinal_position;", tableScanParameters);


                Console.WriteLine("Fields found for table {0}: {1}", tableName, dt.Rows.Count);

                //string output_directory_path = @"C:\test\path";
                //string output_file_path = string.Empty;
                output_file_path = System.IO.Path.Combine(output_directory_path, string.Format("{0}.cs", translateToCodeName(tableName)));

                ////////////////////////////////////////////////////////////////////////////////

                int indentLevel = 0;
                string opt_cls_namespace = "etracks.EntityModels";
                string opt_cls_inheritence = ": _BaseEntityModel";

                Console.WriteLine("output to {0}", output_file_path);
                using (System.IO.StreamWriter sw = new System.IO.StreamWriter(output_file_path))
                {
                    sw.AutoFlush = true;

                    sw.WriteLine("{0}namespace {1}", indent(indentLevel), opt_cls_namespace);
                    sw.WriteLine("{0}{{", indent(indentLevel++)); // open namespace

                    sw.WriteLine("{0}public class {2} {1}", indent(indentLevel), opt_cls_inheritence, translateToCodeName(tableName));
                    sw.WriteLine("{0}{{", indent(indentLevel++)); // open class
                    
                    // Write each field as property here
                    foreach (DataRow dr in dt.Rows)
                    {
                        sw.WriteLine("{0}public {1} {2} {{ get; set; }}", indent(indentLevel), 
                            translateDataType(dr["data_type"].ToString(), dr["is_nullable"].ToString()), 
                            translateToCodeName(dr["column_name"].ToString()));
                        sw.WriteLine();
                    }

                    sw.WriteLine("{0}}} // public class {2} {1}", indent(--indentLevel), opt_cls_inheritence, translateToCodeName(tableName));
                    sw.WriteLine();
                    sw.WriteLine("{0}}} // namespace {1}", indent(--indentLevel), opt_cls_namespace); // close namespace

                }

            }

        }
    }
}
